package NarkyPackage;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Image;
import java.awt.Color;


/**
 * Provee elementos basicos para que todas los elementos contenidos en el juego posean una
 * interfaz en comun y puedan heredar su sistema de dibujo y de proporcionar posicion. Esta
 * clase sabe una imagen que es la que va a dibujar en la pantalla (que puede ser cambiada
 * en cualquier momento), y sabe su posicion y tamano. 
 * 
 * @author GameTECH
 * @version 1.0
 */
public abstract class ElementoDeNarkyKong {

	private Rectangle rectActual, rectFuturo;
	private Image img;
	private Color color;
	protected Juego juego;
	private String bala = "default";
	private int altura = 0;
	private long ID;
	private int tiempoHastaElSiguienteDisparo = 5;
	private boolean puedeDisparar = false;
	private int direccion = 1;

	protected long lastTimeUpdated;

	/**
	 * Crea un elemento de narky kong que se puede dibujar y que va a pertenecer al juego
	 * 'juego'
	 * 
	 * @param juego Juego al que pertenece el elemento
	 */
	public ElementoDeNarkyKong(Juego juego) {
		super();
		ID = (long)(Math.random() * Long.MAX_VALUE);
		this.juego = juego;
		juego.registrarElemento(this);

		rectActual = rectFuturo = new Rectangle ((int)(Math.random()*NarkyApplet.APP_WIDTH),
				(int)(Math.random()*NarkyApplet.APP_HEIGHT),20, 40);

		lastTimeUpdated = System.currentTimeMillis();

		img = null;
		color =new Color ((int)(Math.random()*256),
				(int)(Math.random()*256), (int)(Math.random()*256));
	}

	/**
	 * Dibuja a la imagen que tiene registrada como propia segun la posicion
	 * y tamano que tiene registrado el objeto. 
	 * 
	 * @param g La instancia de Graphics del Juego para dibujarse
	 */
	public void dibujar(Graphics g){

		Rectangle rectIntermedio = this.getRectIntermedio();

		//Si la dirección es hacia la izq -> flipear la imagen
		if (direccion == -1) {
			rectIntermedio.setSize((int)(-1 * rectIntermedio.getWidth()), (int)rectIntermedio.getHeight());
			rectIntermedio.setLocation((int)(rectIntermedio.getX() - rectIntermedio.getWidth()), (int)rectIntermedio.getY());
		}
		if (img == null) {
			g.setColor( color );
			g.fillRect((int)rectIntermedio.getX(), (int)rectIntermedio.getY(), (int)rectIntermedio.getWidth(),
					(int)rectIntermedio.getHeight());
		} else {
			g.drawImage(img, (int)rectIntermedio.getX(), (int)rectIntermedio.getY(),
					(int)rectIntermedio.getWidth(), (int)rectIntermedio.getHeight(), null);
		}
	}

	/**
	 * Regresa un rectangulo intrapolado de acuerdo a la posicion actual, la posicion a la
	 * que se desea ir y el tiempo que ha pasado desde que se inicio el movimiento.
	 * @return El rectangulo
	 */
	public Rectangle getRectIntermedio() {
		Rectangle rectIntermedio = new Rectangle(0,0, (int)rectActual.getWidth(), (int)rectActual.getHeight());
		double cambioEnX = (rectFuturo.getX() - rectActual.getX())/(1000/Juego.ActualizacionesPorSegundo) * (System.currentTimeMillis() - lastTimeUpdated);
		double cambioEnY = (rectFuturo.getY() - rectActual.getY())/(1000/Juego.ActualizacionesPorSegundo) * (System.currentTimeMillis() - lastTimeUpdated);
		rectIntermedio.setLocation((int)(rectActual.getX() + cambioEnX), (int)(rectActual.getY() + cambioEnY));

		return rectIntermedio;
	}

	/**
	 * Compara el rectangulo que recibe con el rectangulo interno donde tiene
	 * su posicion y tamano y regresa true cuando los dos rectangulos se sobreponen. 
	 * 
	 * @param rect El rectangulo de la figura con la que se quiere comparar
	 * @return     Si se sobreponen o no
	 */
	public boolean ocupaEstaPosicion(Rectangle rect) {
		return this.rectFuturo.intersects(rect);
	}

	/**
	 * Metodo protegido solo accesible a las clases que heredan para cambiar la
	 * imagen con la que el juego dibuja a este elemento. Puede ser accesado desde
	 * el constructor solamente para objetos que nunca cambian de imagen o si el
	 * objeto cambia de imagen (ejemplo para cambiar de arma), se puede llamar a
	 * este metodo en cualquier momento.
	 * 
	 * @param img La imagen que se desea para el objeto
	 */
	public void setImage(Image img) {
		this.img = img;
	}

	/**
	 * Metodo protegido, solo accesible a las clases que heredan para definir el
	 * tamano y posicion del objeto. 
	 * 
	 * @param rect La nueva posicion y tamano que se desea tenga el objeto
	 */
	protected void setRect(Rectangle rect) {
		this.rectActual = this.rectFuturo = rect;
	}

	/**
	 * Metodo que regresa la posicion y tamano del elemento
	 * @return instancia de Rectangle con la posicion y el tamano
	 */
	protected Rectangle getRect() {
		return rectActual;
	}

	/**
	 * Mueve al objeto en la pantalla de acuerdo a el numero especificado. Este
	 * metodo es solo para uso de las clases que heredan pues no valida la posicion
	 * a la que se mueve, el metodo es solo para efectos de dibujo.
	 * 
	 * @param x El desplazamiento en pixeles sobre el eje X
	 * @param y El desplazamiento en pixeles sobre el eje Y
	 */
	protected void move (int x, int y) {
		if ((rectFuturo.getX() + x) > juego.getWidth() || 
				(rectFuturo.getX() + x) < 0) 
			juego.removerElemento(this);

		rectFuturo = new Rectangle ((int)rectFuturo.getX() + x, (int)rectFuturo.getY() 
				+ y, (int)rectFuturo.getWidth(), (int)rectFuturo.getHeight());
	}

	/**
	 * Le pregunta al mapa si es posible moverse a esa posicion
	 * @param x
	 * @param y
	 * @return
	 */
	protected boolean mePuedoMoverA(int x, int y) {
		if (rectFuturo.getX() + x + rectFuturo.getWidth() > juego.getWidth() || 
				rectFuturo.getX() + x < 0)
			return false;

		return juego.mapa.mePuedoMover(this, x, y);
	}

	/**
	 * Metodo vacio para que los elementos del juego puedan recibir disparos.
	 * Para elementos que no reciban disparos, el metodo debe estar implementado vacio.
	 */
	public abstract void recibirDisparo();


	/**
	 * Metodo vacio que los elementos pueden utilizar para recibir actualizaciones cada frame.
	 */
	public void actualizar() {
		if (!puedeDisparar) {
			tiempoHastaElSiguienteDisparo--;
			if (tiempoHastaElSiguienteDisparo < 0)
				puedeDisparar = true;
		}

		rectActual = rectFuturo;
		lastTimeUpdated = System.currentTimeMillis();
	}

	/**
	 * Crea una nueva instancia de bala donde la posicion es enfrente del jugador, y el tipo de bala es el tipo
	 * que tiene registrado. 
	 */
	public void disparar() {
		if (puedeDisparar){
			if (!(this.getClass().getName().equals("NarkyPackage.Malo") && ((Malo)this).tipoDeMalo.equalsIgnoreCase("Kong")))
				juego.applet.sonido.play("disparar");
			
			tiempoHastaElSiguienteDisparo = 5;
			puedeDisparar = false;
			int x = (int)(this.getRect().getX());
			if (direccion > 0) {
				x += (int)(this.getRect().getWidth()) + 2;
			} else {
				x -= 2;
			}
			if (this.tipoDeElemento().equals("Jugador"))
				new Bala(bala, direccion, x, (int)this.getRect().getY() + altura,juego, true);
			else 
				new Bala(bala, direccion, x, (int)this.getRect().getY() + altura,juego, false);

		}

	}

	/**
	 * Se acuerda que tipo de bala es la que se quiere disparar.
	 * 
	 * @param bala El tipo de bala que se desea disparar.
	 */
	public void registrarBala(String bala, int altura) {
		this.bala = bala;
		this.altura = altura;
	}

	public long getID() {
		return ID;
	}

	public boolean equals(ElementoDeNarkyKong e) {
		return e.getID() == this.getID();
	}

	public String toString() {
		return "ElementoID: " + ID;
	}

	public void setDireccion(int direccion) {
		this.direccion = direccion;
	}

	public int getDireccion() {
		return direccion;
	}

	public abstract String tipoDeElemento ();
}
